package com.borened.common.core.mybatisplus;

import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.Wrapper;
import com.baomidou.mybatisplus.core.metadata.OrderItem;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService;
import com.borened.common.core.base.BasePageQuery;
import com.borened.common.core.base.BaseQuery;
import com.borened.common.core.base.result.PageResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.Collection;
import java.util.List;

/**
 * 公共Controller方法提取，包括 mybatisplus分页条件查询方法，普通条件查询方法。
 *
 * @param <S> mp处理Service
 * @param <T> T为实体类
 * @author: cch
 * @since: 2022/3/2
 */
@Slf4j
public class BaseController<S extends IService<T>, T> {

    @Autowired
    protected S serviceImpl;

    public S getServiceImpl() {
        return serviceImpl;
    }

    /**
     * 执行分页条件查询
     *
     * @param pageQuery 分页查询对象
     * @return PageResult<T> 对象
     */
    protected PageResult<T> executePage(BasePageQuery pageQuery) {
        Page<T> page = serviceImpl.page(buildPage(pageQuery), buildWrapper(pageQuery));
        return new PageResult<>(page);
    }

    /**
     * 执行条件查询
     * 该方法和 BaseController#executePage 的区别是根据条件查询但不进行分页。
     *
     * @param query 查询对象
     * @return List<T> 实体集合
     */
    protected List<T> executeQuery(BaseQuery query) {
        return serviceImpl.list(buildWrapper(query));
    }

    protected T getById(Serializable id) {
        return serviceImpl.getById(id);
    }

    protected boolean saveBatch(Collection<T> entityList) {
        return serviceImpl.saveBatch(entityList);
    }

    protected boolean saveOrUpdate(T entity) {
        return serviceImpl.saveOrUpdate(entity);
    }

    protected boolean removeByIds(Collection<? extends Serializable> idList) {
        return serviceImpl.removeByIds(idList);
    }

    /**
     * 由自定义分页对象BasePageQuery的子类，构建分页对象Page属性。默认支持单列排序
     * 原理是PaginationInnerInterceptor自动实现分页。
     *
     * @param pageQuery
     * @return
     */
    private Page<T> buildPage(BasePageQuery pageQuery) {
        Page<T> page = new Page<>(pageQuery.getPageNum(), pageQuery.getPageSize());
        String column = StrUtil.toUnderlineCase(pageQuery.getSortCol());
        if (BasePageQuery.SORT_ASC.equalsIgnoreCase(pageQuery.getSort())) {
            page.addOrder(OrderItem.asc(column));
        } else {
            page.addOrder(OrderItem.desc(column));
        }
        return page;
    }

    /**
     * <p>
     * 根据查询参数对象构建指定类型的Wrapper查询条件构造器,</br>
     * 注意返回的Wrapper<T> 实体类型T 根据调用的IService指定的泛型决定。
     * </p>
     *
     * @param queryObj    查询对象
     * @param entityClass 返回的集合实体
     * @return Wrapper<T>
     */
    private Wrapper<T> buildWrapper(BaseQuery queryObj) {
        MpQueryWrapper<T> wrapper = new MpQueryWrapper<>();
        //利用反射实现各字段的条件构造器
        Class<?> queryClazz = queryObj.getClass();
        Field[] fields = queryClazz.getDeclaredFields();

        for (Field field : fields) {
            if (field.isAnnotationPresent(MpWrapper.class)) {
                MpWrapper mpWrapper = field.getAnnotation(MpWrapper.class);
                //字段的值为空或是标注了不支持的运算符 则跳过
                Object val = ReflectUtil.getFieldValue(queryObj, field);
                if (val == null || !MpQueryWrapper.SUPPORTED_KEYWORDS.contains(mpWrapper.operateType())) {
                    continue;
                }
                String column = mpWrapper.column();
                //未指定列名自动使用字段名转小驼峰
                column = StrUtil.isEmpty(column) ? StrUtil.toUnderlineCase(field.getName()) : column;
                if (StrUtil.isNotBlank(mpWrapper.applySql())){
                    wrapper.apply(mpWrapper.applySql(), val);
                }else {
                    wrapper.addCondition(column, mpWrapper.operateType(), val);
                }
                //拼接注入的sql片段
                if (queryObj.getDataScopeSql()!=null){
                    wrapper.apply(queryObj.getDataScopeSql());
                }
            }
        }
        return wrapper;
    }

}
